#include "kcp_server.h"
#include "connection_mgr.h"
#include "type.h"
#include "knet.h"
#include "assert.h"

namespace KCP {
CKCPServer::CKCPServer(asio::io_service& ioService,
	const std::string& ip,uint16 port,
	IKSessionFactory *f, bool listen) {
	// for server and client mode.
	if (listen) {
		assert(port != 0 && "server port error");
		m_connections = new CConnectionMgr(ioService,ip,port);
	} else {
		m_connections = new CConnectionMgr(ioService);
	}

	this->set_callback(std::bind(
		&CKCPServer::event_callback, this, 
		std::placeholders::_1, std::placeholders::_2,
		std::placeholders::_3, std::placeholders::_4)
	);
	m_sessionFactory = f;
	assert(m_sessionFactory != nullptr && "session factory is nil");

	if (listen) {
		m_connections->setMode(CConnectionMgr::mode_server);
	} else {
		m_connections->setMode(CConnectionMgr::mode_client);
	}
}

CKCPServer::~CKCPServer() {
	if (m_connections != nullptr) {
		delete m_connections;
		m_connections = nullptr;
	}
}

int CKCPServer::event_callback(CConnection *conn,
	eEventType eventType,const char* msg,int len) {
	int ret = 0;
	// Enum all event and deal it.
	switch (eventType) {
	case eRcvMsg: {
		bool recvRet = this->onRecv(conn, msg, len);
		if (!recvRet) {
			ret = -1;
		} else {
			ret = 0;
		}
	}
	break;

	case eConnect: {
		this->onEstablish(conn, msg, len);
		ret = 0;
	}
	break;

	case eDisconnect: {
		this->onClose(conn);
		ret = 0;
	}
	break;

	default:
		ret = 1;
	}
	return ret;
}

void CKCPServer::update() {
	m_connections->update();
}

void CKCPServer::set_callback(const std::function<event_callback_t>& func) {
	m_connections->set_callback(func);
}

void CKCPServer::stop() {
	m_connections->stop();
}

bool CKCPServer::onRecv(CConnection *conn, const char *data, int len) {
	if (data == nullptr) {
		ADebug("Recv data is null");
		return false;
	}

	if (conn == nullptr) {
		ADebug("connection is null");
		return false;
	}

	auto session = conn->getSession();
	if (session == nullptr) {
		ADebug("connection session is null");
		return false;
	} else {
		return session->onRecv(data, len);
	}
}

void CKCPServer::onClose(CConnection *conn) {
	auto session = conn->getSession();
	assert(session != nullptr && "session is null");
	if (session == nullptr) {
		return;
	}
	
	session->onClose();
	session->release();
}

void CKCPServer::onEstablish(CConnection *conn, const char *data, int len) {
	auto session = m_sessionFactory->CreateSession();
	assert(session != nullptr && "session is null");
	if (session == nullptr) {
		return;
	}

	conn->setSession(session);
	session->setConnection(conn);
	session->onEstablish();
}

void CKCPServer::sendMsg(const char *ip, uint16 port, const char *msg, int len) {
	auto &&asioEndPoint = udp::endpoint(asio::ip::address::from_string(ip), port);
	m_connections->send_udp(msg,len,asioEndPoint);
}

CConnectionMgr* CKCPServer::getConnMgr() {
	return m_connections;
}

size_t CKCPServer::GetConnectionSize() {
	return this->getConnMgr()->allConnectionSize();
}

void CKCPServer::Stop() {
	this->stop();
}
}